﻿using System;
using System.Reflection;
using System.Linq.Expressions;

namespace EaseDapper.Utilities
{
    public static class GetDataExtensions
    {
        public static object GetMemberValue(Expression expression)
        {
            try
            {
                object value = null;
                if (expression is MemberExpression me)
                {
                    try
                    {
                        if (me.Member.MemberType.Equals(MemberTypes.Property))
                        {
                            value = GetPropertyValue(me);
                        }
                        else if (me.Member.MemberType.Equals(MemberTypes.Field))
                        {
                            value = GetFiledValue(me);
                        }
                    }
                    catch
                    {
                        try
                        {
                            value = DynamicInvoke(me);
                        }
                        catch
                        {
                            value = me.Member.Name;
                        }
                    }
                }
                return value;
            }
            catch (Exception)
            {
                throw new ArgumentException(null, nameof(expression));
            }
        }

        public static object DynamicInvoke(Expression expression, MemberExpression memberExpression = null)
        {
            try
            {
                object value = Expression.Lambda(expression).Compile().DynamicInvoke();
                if (value != null && value.GetType().IsClass() && value.GetType() != typeof(string) && memberExpression != null)
                {
                    value = Expression.Lambda(memberExpression).Compile().DynamicInvoke();
                }
                if (value != null && value.GetType().IsEnum())
                {
                    value = Convert.ToInt32(value);
                }
                return value;
            }
            catch (InvalidOperationException)
            {
                throw new InvalidOperationException();
            }
            catch (Exception ex)
            {
                throw new Exception("No support " + expression.ToString() + " " + ex.Message);
            }
        }

        public static object GetPropertyValue(MemberExpression memberExpr)
        {

            if (!(memberExpr.Member is PropertyInfo))
            {
                return DynamicInvoke(memberExpr);
            }
            PropertyInfo pro = (PropertyInfo)memberExpr.Member;
            object reval = pro.GetValue(memberExpr.Member, null);
            if (reval != null && reval.GetType().IsClass() && reval.GetType() != typeof(string))
            {
                var fieldName = memberExpr.Member.Name;
                var proInfo = reval.GetType().GetProperty(fieldName);
                if (proInfo != null)
                {
                    reval = proInfo.GetValue(reval, null);
                }
                var fieInfo = reval.GetType().GetField(fieldName);
                if (fieInfo != null)
                {
                    reval = fieInfo.GetValue(reval);
                }
                if (fieInfo == null && proInfo == null)
                {
                    throw new Exception(string.Format(" Property \"{0}\" can't be private ", pro.Name));
                }
            }
            return reval;
        }

        public static object GetFiledValue(MemberExpression memberExpr)
        {
            if (!(memberExpr.Member is FieldInfo))
            {
                return DynamicInvoke(memberExpr);
            }
            FieldInfo field = (FieldInfo)memberExpr.Member;
            object reval = field.GetValue(memberExpr.Member);
            if (reval != null && reval.GetType().IsClass() && reval.GetType() != typeof(string))
            {
                var fieldName = memberExpr.Member.Name;
                var proInfo = reval.GetType().GetProperty(fieldName);
                if (proInfo != null)
                {
                    reval = proInfo.GetValue(reval, null);
                }
                var fieInfo = reval.GetType().GetField(fieldName);
                if (fieInfo != null)
                {
                    reval = fieInfo.GetValue(reval);
                }
                if (fieInfo == null && proInfo == null)
                {
                    if (field.IsPrivate)
                    {
                        throw new Exception(string.Format(" Field \"{0}\" can't be private ", field.Name));
                    }
                }
            }
            return reval;
        }
    }
}
